Next | Prev | Up | Top | Contents | Index

SCSI Device Interface Overview

The SCSI host adapter communicates with devices, known as targets, on the SCSI bus. Each SCSI target can control a number of actual devices, known as logical units. Most SCSI targets, however, control a single device.

There are two types of SCSI drivers: device level drivers and host adapter drivers. The host adapter drivers handle the low-level communication over the SCSI interface, such as programming the SCSI interface chip or board, negotiating synchronous or wide mode, and handling disconnect/reconnect. The device drivers handle high-level communication, primarily by issuing SCSI commands and interpreting sense data.

Some examples of host adapter drivers are wd93, wd95, and jag. Examples of device level drivers are dksc, tpsc, and smfd.

There are four basic interfaces used to communicate to a host adapter driver: scsi_info, scsi_alloc, scsi_free, and scsi_command(). Each of these interfaces is implemented as an array of pointers to functions, indexed by a host adapter driver number. The host adapter driver number is determined from the adapter number. The adapter number ranges are defined in sys/scsi.h, and are different on different architectures. In general, Integral SCSI controllers start at adapter 0, and Interphase VME-SCSI (Jaguar) controllers start after the last Integral adapter. Each SCSI bus is considered to be one adapter.

On a POWER Series or Crimson system, for example, Interphase VME-SCSI controller 3, bus 0, would be adapter number 10. On a CHALLENGE/Onyx system, VME-SCSI controller 3, bus 0, would be adapter number 134. See the definitions of SCSI_SGISTART, SCSI_SGICOUNT, SCSI_JAGSTART, and SCSI_JAGCOUNT in sys/scsi.h.


Determining Driver Number

To determine the correct number for your driver, see scsi_driver_table in
sys/scsi.h. scsi_driver_table is an array, indexed by adapter number, that returns the adapter type, which is a constant defined by the SCSIDRIVER_XXX definitions. scsi_driver_table may be sparsely populated to accommodate possible options. This avoids the reassignment of major and minor device numbers in cases where hardware is added at a later time.

SCSI_XXXSTART definitions define the starting adapter number for the various adapter types. SCSI_XXXCOUNT is the number of possible adapters for a given controller type. Both SCSIDRIVER_WD93 and SCSIDRIVER_WD95 are part of Silicon Graphics-built SCSI controllers, and so use the SCSI_SGISTART and SCSI_SGICOUNT #define's.

For example, on a CHALLENGE system, the second bus on Interphase VME-SCSI controller 4 would be considered adapter number 137. Counting SCSI_JAGSTART as 128, (controller 4 * 2 adapters per controller) + bus 1 (the second bus on the controller) gives 9 to be added to the value of SCSI_JAGSTART to give adapter (or bus) number 137 from the point of view of a SCSI device driver.

Typically, the major and minor numbers of a device are used to determine the adapter number, which can then be used to index into scsi_driver_table.

adap = sdk_adap_num(device);

driver_num = scsi_driver_table[adap];


scsi_info - Getting Information About a Device

Before your driver tries to access a device, it must call the host adapter scsi_info function. The host adapter driver then issues an Inquiry command to the given device and returns a pointer to a struct scsi_target_info. If the Inquiry is not successful -- or if the adapter, target, or lun is invalid -- the return value is NULL. The SCSI device driver must examine the inquiry data to determine whether it is the appropriate driver for the device.

Synopsis

struct scsi_target_info * (*scsi_info[])
                         (u_char adapter, u_char target, u_char lun);
Arguments

adapter

The adapter number.

target

The target number.

lun

The logical unit number.
Example

The following would be the way to use scsi_info to get information about target 5, LUN 0 on Interphase controller 4, bus 1 (the second bus):

info = (*scsi_info[SCSIDRIVER_JAG])(9, 5, 0);


scsi_alloc - Initializing a Connection

Before your driver can issue a command to a SCSI device through scsi_command, it must have the low-level driver initialize the connection. The interface to this low-level driver is through the scsi_alloc(), typically in the drvopen() routine.

Synopsis

int (*scsi_alloc[driver_num]) (unsigned char adap, unsigned char target, unsigned char lun, int option, void (*callback)());

Arguments

adap

The number of the SCSI adapter for the device you want to control. All IRIX systems support at least one adapter (0). Systems with POWERchannel have two or four built-in adapters and up to 16 VME-SCSI adapters (two per controller). CHALLENGE/Onyx systems can have up to 120 different built-in SCSI adapters (although one system can have only a fraction of those installed at one time) and 16 VME-SCSI adapters; Indigo2 systems have two adapters.

target

The target ID of the SCSI controller for the device your driver wants to control. This must be a value from 0 to 15, but cannot be the ID of the controller itself (typically 0 for built-in and 7 for the Jaguar). Not all host adapter implementations support SCSI device numbers 8 through 15. (Note that for this purpose, the Jaguar VME_SCSI controller has two SCSI host adapters, so only devices 0-7 are valid. Choose the bus number with adap.) Usually, the device is configured to a fixed ID by switch settings or jumpers. Refer to the device technical specification for details. If the system contains more than one of a particular device, the device driver normally uses the minor device number to distinguish between devices.

lun

The logical unit number for the device your driver wants to control. This is often 0 because most SCSI targets support only a single logical unit.

option

Two options are available to scsi_alloc:

SCSIALLOC_EXCLUSIVE specifies that the device driver wants to have exclusive access to the target in question. If any other device has allocated a connection, the scsi_alloc call fails.

SCSIALLOC_QDEPTH, the queue depth that your driver requests, is the bottom eight bits of option. It is considered advice only, and may or may not be followed.

callback

Gets a pointer to a function that the host adapter driver calls every time it has sense data. Only one driver at a time may have a callback allocated, so, in this respect, it functions like SCSIALLOC_EXCLUSIVE in the option argument above. If the callback argument is not NULL, the host adapter driver calls it with a pointer to the sense data (an array of char) every time there is any. This allows multiple drivers to access a device while still allowing one of them to keep apprised of all sense data.
Returns

If the call to scsi_alloc is successful, the type of SCSI adapter being used (SCSIDRIVER_WD93, SCSIDRIVER_WD95, or SCSIDRIVER_JAG) is returned. Otherwise, this routine returns 0, which indicates that no connection could be established between the host adapter driver and your driver, probably because the arguments were included for the adapter.


scsi_command - Executing a SCSI Command

Once your driver has established a connection to a host adapter driver with a successful call to scsi_alloc[](), your driver can send SCSI commands to the device. To send SCSI commands to a device, fill out a scsi_request structure and then call scsi_command[](), passing it the address of this structure. The system uses this structure to report back on the status of the SCSI command.

Not all scsi_request type structure members are of interest to your device driver (some members are of interest only to the SCSI host adapter driver and may change across releases of the operating system). Following is the definition of the structure scsi_request:

struct scsi_request
{
        /* values filled in by device driver */
        u_char   sr_ctlr;
        u_char   sr_target;
        u_char   sr_lun;
        u_char   sr_tag;     /* first byte of tag message */

        u_char  *sr_command; /* scsi command */
        ushort   sr_cmdlen;  /* length of scsi command */
        ushort   sr_flags;   /* direction of data transfer */
        ulong    sr_timeout; /* in seconds */

        u_char  *sr_buffer;  /* location of data */
        uint     sr_buflen;  /* amount of data to transfer */
        u_char  *sr_sense;   /* where to put sense data in 
                             /*case of CC */
        ulong    sr_senselen;/* size of buffer allocated for 
                             /*sense data */
        void    (*sr_notify)(struct scsi_request *);
                             /* callback pointer */
        void    *sr_bp;      /* usually a buf_t pointer */

        /* spare pointer used by device driver */
        void    *sr_dev;

        /* spare fields used by host adapter driver */
        void    *sr_ha;      /* usually used for linked list 
                             /*of req's */
        void    *sr_spare;   /* used as spare pointer, int, 
                             /*etc. */

        /* results filled in by host adapter driver */
        uint     sr_status;      /* Status of command */
        u_char   sr_scsi_status; /* SCSI status byte */
        u_char   sr_ha_flags;    /* flags used by host 
                                 /*adapter driver */
        short    sr_sensegotten; /* number of sense bytes 
                                 /* received; -1 == err */
        uint     sr_resid;       /* amount of sr_buflen not 
                                 /*transferred */
};
typedef struct scsi_request      scsi_request_t;
Before calling scsi_command[](), your driver must fill in the values indicated above. Some are optional.

sr_ctlr

Number of the adapter where the command will be transferred.

sr_target

ID of the target where the command will be transferred.

sr_lun

Number of the logical unit where the command will be transferred.

sr_tag

Type of queue tag to use (see SCSI-2 specification). Not all tag types are supported by all drivers.

sr_command

Give this member a pointer to the SCSI command descriptor block that you want to send to the device.

sr_cmdlen

The length (in bytes) of the command to which the sr_command member points. You can use three symbolic constants for this member (the constants are defined in sys/scsi.h), but other values are permitted for vendor unique commands, too:

SC_CLASS0_SZ is a command size of 6 bytes.

SC_CLASS1_SZ is a command size of 10 bytes.

SC_CLASS2_SZ is a command size of 12 bytes.

sr_flags

This member must set zero or more options as specified below:

SRF_DIR_IN -- Data associated with the command will be written into memory.

SRF_FLUSH -- A cache operation is required on the data.

SRF_MAP -- The sr_buffer address needs to be mapped -- it is a kernel virtual address.

SRF_MAPBP -- sr_bp has a pointer to a struct buf that is not mapped (BP_ISMAPPED returns false).

SRF_AEN_ACK -- This request acknowledges an error in a previous request. Once a scsi_request is returned with an error, all further requests are returned with SC_ATTN, without being acted upon, until SRF_AEN_ACK is set.

SRF_NEG_ASYNC -- This request attempts to negotiate async xfer mode on this command (if currently using sync xfers).

SRF_NEG_SYNC -- This request attempts to negotiate sync xfer mode on this command if currently asynchronous. It may be ignored by some adapter drivers, either always or if the driver has previously failed to negotiate sync xfer mode with this target. This overrides the SCSIALLOC_NOSYNC flag to scsi_alloc (if it has been specified).

sr_timeout

The maximum amount of time, in Hz, that a command can take.

sr_buffer

A pointer to the start of the data associated with a command. If there is no data (as with a test_unit_ready for instance), sr_buffer must be NULL.

sr_buflen

The maximum amount of data that can be transferred. This is not necessarily the amount that will be transferred, but is an upper limit.

sr_sense

A pointer to a buffer where request sense data can be copied in case a command gets a check condition status.

sr_senselen

The amount of space reserved for request sense data.

sr_notify

A pointer to a notification routine. The notification routine gets called when a command is complete or gets an error. This value must be set (NULL is not a valid value).

sr_bp

This field must point to the struct buf corresponding that generated the scsi_request if the SRF_MAPBP flag is set in sr_flags. If the SRF_MAPBP flag is not set, then sr_bp is not used by the host adapter driver.

sr_dev

This field is never used by a host adapter driver and is reserved for the use of your device driver.
After control returns from the host adapter driver to your driver, your driver must check the following members of the scsi_request type structure. These members are:

sr_status

This member reports the overall status of the SCSI command (this is the host adapter driver status; SCSI bus status is in sr_scsi_status). sr_status can report one of these values:

SC_GOOD -- indicates no error. The bus successfully processed the SCSI command; however, the command itself may still have failed (see sr_scsi_status).

SC_TIMEOUT -- indicates selection timeout. The device did not respond to selection within 250 milliseconds.

SC_HARDERR -- indicates hardware or SCSI device error, usually a SCSI bus reset, possibly caused by a bad phase or time-out on some other device.

SC_PARITY -- indicates an unrecoverable parity error on the SCSI bus.

SC_MEMERR -- indicates an unrecoverable parity or ECC error from host memory.

SC_CMDTIME -- indicates the command did not complete before the time-out specified in s_timeoutval elapsed.

SC_ALIGN -- indicates the buffer address did not meet the alignment requirements of the system. Most Silicon Graphics systems require starting buffer addresses to be on a four-byte boundary.

SC_ATTN -- indicates the scsi_request() has been aborted by a SCSI bus reset, the device, or the driver, due to an error in another request.

SC_REQUEST -- indicates the request is not a valid request. This can be because no scsi_alloc has been done successfully or because the scsi_request() has conflicting or illegal values, such as sr_notify being set to NULL.

sr_scsi_status

The SCSI status byte sent by the target. sr_scsi_status can report any one of these values: The values of sr_sci_status correspond to those described in the SCSI specification:

ST_GOOD -- indicates the target has successfully completed the SCSI command. On this value, sr_sensegotten must be checked to see if a check condition occurred on the command.

ST_CHECK -- indicates that the request sense command generated by the host adapter driver in response to a check condition also got a check condition. This is a special case. If a device reports a "check condition" status, the host adapter driver automatically issues a request sense command and reports the status of that command in this byte. If sr_sense is set and sr_senselen is > 0, sr_sensegotten is set to the number of bytes of sense data received, or to -1 if an error occurs during the request sense command.

ST_COND_MET -- indicates search condition satisfied.

ST_BUSY -- indicates the target is busy. Normally, the driver should delay, then reissue the command.

ST_INT_GOOD -- indicates this status is reported for every command in a series of linked commands. Although they are not supported, these linked commands may sometimes work.

ST_RES_CONF -- indicates an attempt to access a logical unit or an extent within a logical unit that reserves that type of access to another SCSI device.

This byte corresponds to the SCSI command status byte as documented in the SCSI specifications.

sr_sensegotten

The number of bytes of sense data gotten as a result of a request sense command issued in response to a check condition status from this command, if any, or -1 if a check condition occurs, but the request sense command fails.

sr_resid

The difference between sr_buflen and the number of bytes actually transferred.

scsi_free - Freeing the Connection

When your driver is done with a SCSI device, it must call through the array scsi_free to indicate to the host adapter driver that your driver no longer needs to use the device. Typically, you do this in your drvclose() routine.

Synopsis

int (*scsi_free[driver_num]) (unsigned char adap, unsigned char target, unsigned char lun, void (*callback)());

The arguments to scsi_free are similar to those to scsi_alloc except that no option argument is used.


Next | Prev | Up | Top | Contents | Index